/**
* @file gdt.cpp
* Global Description Table Interface Implementation for X86 architecture.
* Provides the interface for managing GDT over HAL. GDT handles basic
* memory map and mapadding it with descriptors.
*
* Copyrights 2010 Michal Saman, m.saman@designplus.cz
* This source code is release under the Apache License 2.0.
* http://www.apache.org/licenses/LICENSE-2.0
*/

#include <string.h>
#include <stdint.h>
#include <hal/gdt.h>

#pragma pack (push, 1)
/**
* Struture representing the gdtr processor register.
*/
struct Gdtr {
	uint16_t	m_limit;		/**< size of GDT */
	uint32_t	m_base;			/**< base addres of gdt */
};
#pragma pack (pop)

/** This is our GDT - it is array of GdtDescriptors. */
static struct GdtDescriptor gdt[GDT_MAX_DESCRIPTORS];
static struct Gdtr	gdtr;

/** 
* Method sets memory address of GDT to special processor register - lgdt.
*/
static void gdt_install() {
	__asm__ volatile ("lgdt	%0" : : "m"(gdtr) );
};

/** 
* Add descriptor to specified order to global descriptor table.
* @param i		Order of descritor of global descriptor table.
* @param base	Base address of memory that descriptor specified.
* @param limit	Size in bytes of memory area that descriptor specified.
* @param access	Access flags for specified memory area.
* @param grand	Granduality flags for specified memory area.
*/
void gdt_set_descriptor(uint32_t i, uint64_t base, uint64_t limit, uint8_t access, uint8_t grand) {
	if (i > GDT_MAX_DESCRIPTORS)
		return;
	//zero-fill descriptor
	memset((void*)&gdt[i], 0, sizeof(struct GdtDescriptor));
	gdt[i].baseLo = (uint16_t)(base & 0xFFFF);
	gdt[i].baseMid = (uint8_t)((base>>16) & 0xFF);
	gdt[i].baseHi = (uint8_t)((base>>24) & 0xFF);
	gdt[i].limit = (uint16_t)(limit & 0xFFFF);
	gdt[i].flags = access;
	gdt[i].grand = (uint8_t)((limit>>16)&0x0F);
	gdt[i].grand |= grand & 0xF0;
};

/**
* Function returns memory address for specified descriptor of GDT.
* @param i		Number of descriptor, of what we want to get memory address.
* @return		Address to memory wher GdtDescriptor is located.
*/
struct GdtDescriptor *gdt_get_descriptor(int i) {
	if (i > GDT_MAX_DESCRIPTORS)
		return 0;
	return &gdt[i];
};

/**
* Function inicialize (create) default GDT and load it to processor register.
* Function set default NULL descriptor, code descriptor and data descriptor. Than call
* gtd_install() to set special processor register, that point to GDT in memory.
*/
int gdt_init() {
	// size of GDT in memory
	gdtr.m_limit = (sizeof(struct GdtDescriptor) * GDT_MAX_DESCRIPTORS)-1;
	// base addres of GDT in memory
	gdtr.m_base = (uint32_t) &gdt[0];
	// set NULL descriptors
	gdt_set_descriptor(0, 0, 0, 0, 0);
	// set default code descriptor
	gdt_set_descriptor(1, 0, 0xFFFFFFFF, GDT_DESC_READWRITE|GDT_DESC_EXEC_CODE|GDT_DESC_CODEDATA|GDT_DESC_MEMORY,
		GDT_GRAND_4K|GDT_GRAND_32BIT|GDT_GRAND_LIMITHI_MASK);
	// set default data descriptor
	gdt_set_descriptor(2, 0, 0xFFFFFFFF, GDT_DESC_READWRITE|GDT_DESC_CODEDATA|GDT_DESC_MEMORY,
		GDT_GRAND_4K|GDT_GRAND_32BIT|GDT_GRAND_LIMITHI_MASK);
	// set GDTR register in proccessor
	gdt_install();
	return 0;
};
